//===--- MetadataLayout.cpp - Metadata construct layout -------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file includes code for laying out type metadata.
//
//  It also implements certain low-level access routines for type metadata.
//  These routines are generally declared in one of two different places:
//
//    - Mid-level routines to extract data from metadata are declared in
//      GenMeta.h.  This file is a sort of sub-module of GenMeta.cpp.
//
//    - Low-level routines to project the addresses of fields in metadata
//      are declared in MetadataLayout.h.
//
//===----------------------------------------------------------------------===//

#include "polarphp/irgen/internal/MetadataLayout.h"
#include "polarphp/irgen/internal/GenMeta.h"

#include "polarphp/irgen/internal/ClassMetadataVisitor.h"
#include "polarphp/irgen/internal/EnumMetadataVisitor.h"
#include "polarphp/irgen/internal/IRGenFunction.h"
#include "polarphp/irgen/internal/StructMetadataVisitor.h"
#include "polarphp/irgen/internal/ForeignClassMetadataVisitor.h"
#include "polarphp/irgen/internal/TupleMetadataVisitor.h"

#include "polarphp/basic/LLVM.h"
#include "llvm/ADT/Optional.h"

using namespace polar;
using namespace irgen;

namespace {

template <class Impl, template <class> class Base>
class LayoutScanner : public Base<Impl> {
   Optional<Size> AddressPoint;

protected:
   Optional<Size> DynamicOffsetBase;

   template <class... As>
   LayoutScanner(As &&... args) : Base<Impl>(std::forward<As>(args)...) {}

public:
   using StoredOffset = MetadataLayout::StoredOffset;

   void noteAddressPoint() { AddressPoint = this->NextOffset; }
   StoredOffset getNextOffset() const {
      if (DynamicOffsetBase) {
         return StoredOffset(this->NextOffset - *DynamicOffsetBase,
                             StoredOffset::Dynamic);
      }

      return StoredOffset(this->NextOffset - *AddressPoint,
                          StoredOffset::Static);
   }

   Size getAddressPoint() const {
      return *AddressPoint;
   }

   MetadataSize getMetadataSize() const {
      assert(AddressPoint.hasValue() && !AddressPoint->isInvalid()
             && "did not find address point?!");
      assert(*AddressPoint < this->NextOffset
             && "address point is after end?!");
      return {this->NextOffset, *AddressPoint};
   }
};

}

ClassMetadataLayout &IRGenModule::getClassMetadataLayout(ClassDecl *decl) {
   assert(!decl->isForeign() && "Use getForeignMetadataLayout()");
   return cast<ClassMetadataLayout>(
      getMetadataLayout(static_cast<NominalTypeDecl*>(decl)));
}

ForeignClassMetadataLayout &IRGenModule::getForeignMetadataLayout(
   ClassDecl *decl) {
   assert(decl->isForeign() && "Use getMetadataLayout()");
   return cast<ForeignClassMetadataLayout>(
      getMetadataLayout(static_cast<NominalTypeDecl*>(decl)));
}

EnumMetadataLayout &IRGenModule::getMetadataLayout(EnumDecl *decl) {
   return cast<EnumMetadataLayout>(
      getMetadataLayout(static_cast<NominalTypeDecl*>(decl)));
}

StructMetadataLayout &IRGenModule::getMetadataLayout(StructDecl *decl) {
   return cast<StructMetadataLayout>(
      getMetadataLayout(static_cast<NominalTypeDecl*>(decl)));
}

NominalMetadataLayout &IRGenModule::getNominalMetadataLayout(
   NominalTypeDecl *decl) {
   return cast<NominalMetadataLayout>(getMetadataLayout(decl));
}

MetadataLayout &IRGenModule::getMetadataLayout(NominalTypeDecl *decl) {
   auto &entry = MetadataLayouts[decl];
   if (!entry) {
      if (auto theClass = dyn_cast<ClassDecl>(decl)) {
         if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType)
            entry = new ForeignClassMetadataLayout(*this, theClass);
         else
            entry = new ClassMetadataLayout(*this, theClass);
      } else if (auto theEnum = dyn_cast<EnumDecl>(decl)) {
         entry = new EnumMetadataLayout(*this, theEnum);
      } else if (auto theStruct = dyn_cast<StructDecl>(decl)) {
         entry = new StructMetadataLayout(*this, theStruct);
      } else {
         llvm_unreachable("bad nominal type!");
      }
   }
   return *cast<MetadataLayout>(entry);
}

void IRGenModule::destroyMetadataLayoutMap() {
   for (auto &entry : MetadataLayouts) {
      entry.second->destroy();
   }
}

void MetadataLayout::destroy() const {
   switch (getKind()) {
      case Kind::Class:
         delete cast<ClassMetadataLayout>(this);
         return;

      case Kind::Struct:
         delete cast<StructMetadataLayout>(this);
         return;

      case Kind::Enum:
         delete cast<EnumMetadataLayout>(this);
         return;

      case Kind::ForeignClass:
         delete cast<ForeignClassMetadataLayout>(this);
         return;
   }
   llvm_unreachable("bad kind");
}

/******************************* NOMINAL TYPES ********************************/

Offset NominalMetadataLayout::emitOffset(IRGenFunction &IGF,
                                         StoredOffset offset) const {
   assert(offset.isValid());

   if (offset.isStatic())
      return Offset(offset.getStaticOffset());

   Address layoutAddr(
      IGF.IGM.getAddrOfClassMetadataBounds(cast<ClassDecl>(getDecl()),
                                           NotForDefinition),
      IGF.IGM.getPointerAlignment());

   auto offsetBaseAddr = IGF.Builder.CreateStructGEP(layoutAddr, 0, Size(0));

   // FIXME: Should this be an invariant load?
   llvm::Value *offsetVal = IGF.Builder.CreateLoad(offsetBaseAddr, "base");

   auto relativeOffset = offset.getRelativeOffset().getValue();
   if (relativeOffset != 0) {
      offsetVal = IGF.Builder.CreateAdd(offsetVal,
                                        llvm::ConstantInt::get(IGF.IGM.SizeTy,
                                                               relativeOffset));
   }

   return Offset(offsetVal);
}

Size
NominalMetadataLayout::getStaticGenericRequirementsOffset() const {
   return GenericRequirements.getStaticOffset();
}

Offset
NominalMetadataLayout::getGenericRequirementsOffset(IRGenFunction &IGF) const {
   return emitOffset(IGF, GenericRequirements);
}

static llvm::Value *emitLoadOfGenericRequirement(IRGenFunction &IGF,
                                                 llvm::Value *metadata,
                                                 NominalTypeDecl *decl,
                                                 unsigned reqtIndex,
                                                 llvm::Type *reqtTy) {
   auto offset =
      IGF.IGM.getNominalMetadataLayout(decl).getGenericRequirementsOffset(IGF);
   offset = offset.offsetBy(IGF, Size(reqtIndex * IGF.IGM.getPointerSize()));

   auto slot = IGF.emitAddressAtOffset(metadata, offset, reqtTy,
                                       IGF.IGM.getPointerAlignment());
   auto witness = IGF.emitInvariantLoad(slot);
   return witness;
}

/// Given a reference to nominal type metadata of the given type,
/// derive a reference to the nth argument metadata.  The type must
/// have generic arguments.
llvm::Value *irgen::emitArgumentMetadataRef(IRGenFunction &IGF,
                                            NominalTypeDecl *decl,
                                            const GenericTypeRequirements &reqts,
                                            unsigned reqtIndex,
                                            llvm::Value *metadata) {
   assert(reqts.getRequirements()[reqtIndex].Interface == nullptr);
   return emitLoadOfGenericRequirement(IGF, metadata, decl, reqtIndex,
                                       IGF.IGM.TypeMetadataPtrTy);
}

/// Given a reference to nominal type metadata of the given type,
/// derive a reference to a protocol witness table for the nth
/// argument metadata.  The type must have generic arguments.
llvm::Value *irgen::emitArgumentWitnessTableRef(IRGenFunction &IGF,
                                                NominalTypeDecl *decl,
                                                const GenericTypeRequirements &reqts,
                                                unsigned reqtIndex,
                                                llvm::Value *metadata) {
   assert(reqts.getRequirements()[reqtIndex].Interface != nullptr);
   return emitLoadOfGenericRequirement(IGF, metadata, decl, reqtIndex,
                                       IGF.IGM.WitnessTablePtrTy);
}

Address irgen::emitAddressOfFieldOffsetVector(IRGenFunction &IGF,
                                              llvm::Value *metadata,
                                              NominalTypeDecl *decl) {
   auto &layout = IGF.IGM.getMetadataLayout(decl);
   auto offset = [&]() {
      if (isa<ClassDecl>(decl)) {
         return cast<ClassMetadataLayout>(layout)
            .getFieldOffsetVectorOffset(IGF);
      } else {
         assert(isa<StructDecl>(decl));
         return cast<StructMetadataLayout>(layout)
            .getFieldOffsetVectorOffset();
      }
   }();

   auto *elementSize = IGF.IGM.SizeTy;
   if (isa<StructDecl>(decl))
      elementSize = IGF.IGM.Int32Ty;

   return IGF.emitAddressAtOffset(metadata, offset, elementSize,
                                  IGF.IGM.getPointerAlignment());
}

/********************************** CLASSES ***********************************/

ClassMetadataLayout::ClassMetadataLayout(IRGenModule &IGM, ClassDecl *decl)
   : NominalMetadataLayout(Kind::Class, decl), NumImmediateMembers(0) {

   struct Scanner : LayoutScanner<Scanner, ClassMetadataScanner> {
      using super = LayoutScanner;

      ClassMetadataLayout &Layout;

      Scanner(IRGenModule &IGM, ClassDecl *decl, ClassMetadataLayout &layout)
         : super(IGM, decl), Layout(layout) {}

      void noteResilientSuperclass() {
         Layout.HasResilientSuperclass = true;
      }

      void noteStartOfImmediateMembers(ClassDecl *forClass) {
         // If our superclass is resilient to us, or the class itself is resilient
         // to us, we will access metadata members relative to a base offset.
         if (forClass == Target) {
            Layout.StartOfImmediateMembers = getNextOffset();

            if (Layout.HasResilientSuperclass ||
                IGM.hasResilientMetadata(forClass, ResilienceExpansion::Maximal)) {
               assert(!DynamicOffsetBase);
               DynamicOffsetBase = NextOffset;
            }
         }
      }

      void addClassSize() {
         Layout.MetadataSize = getNextOffset();
         super::addClassSize();
      }

      void addClassAddressPoint() {
         Layout.MetadataAddressPoint = getNextOffset();
         super::addClassAddressPoint();
      }

      void addInstanceSize() {
         Layout.InstanceSize = getNextOffset();
         super::addInstanceSize();
      }

      void addInstanceAlignMask() {
         Layout.InstanceAlignMask = getNextOffset();
         super::addInstanceAlignMask();
      }

      void noteStartOfGenericRequirements(ClassDecl *forClass) {
         if (forClass == Target)
            Layout.GenericRequirements = getNextOffset();
         super::noteStartOfGenericRequirements(forClass);
      }

      void addGenericWitnessTable(ClassDecl *forClass) {
         if (forClass == Target) {
            Layout.NumImmediateMembers++;
         }
         super::addGenericWitnessTable(forClass);
      }

      void addGenericArgument(ClassDecl *forClass) {
         if (forClass == Target) {
            Layout.NumImmediateMembers++;
         }
         super::addGenericArgument(forClass);
      }

      void addMethod(PILDeclRef fn) {
         if (fn.getDecl()->getDeclContext() == Target) {
            Layout.NumImmediateMembers++;
            Layout.MethodInfos.try_emplace(fn, getNextOffset());
         }
         super::addMethod(fn);
      }

      void noteStartOfFieldOffsets(ClassDecl *forClass) {
         if (forClass == Target)
            Layout.FieldOffsetVector = getNextOffset();
         super::noteStartOfFieldOffsets(forClass);
      }

      void addFieldOffset(VarDecl *field) {
         if (field->getDeclContext() == Target) {
            Layout.NumImmediateMembers++;
            Layout.FieldOffsets.try_emplace(field, getNextOffset());
         }
         super::addFieldOffset(field);
      }

      void addFieldOffsetPlaceholders(MissingMemberDecl *placeholder) {
         if (placeholder->getDeclContext() == Target) {
            Layout.NumImmediateMembers +=
               placeholder->getNumberOfFieldOffsetVectorEntries();
         }
         super::addFieldOffsetPlaceholders(placeholder);
      }

      void addVTableEntries(ClassDecl *forClass) {
         if (forClass == Target)
            Layout.VTableOffset = getNextOffset();
         super::addVTableEntries(forClass);
      }

      void layout() {
         super::layout();
         Layout.TheSize = getMetadataSize();
      }
   };

   Scanner(IGM, decl, *this).layout();
}

Size ClassMetadataLayout::getMetadataSizeOffset() const {
   assert(MetadataSize.isStatic());
   return MetadataSize.getStaticOffset();
}

Size ClassMetadataLayout::getMetadataAddressPointOffset() const {
   assert(MetadataAddressPoint.isStatic());
   return MetadataAddressPoint.getStaticOffset();
}

Size ClassMetadataLayout::getInstanceSizeOffset() const {
   assert(InstanceSize.isStatic());
   return InstanceSize.getStaticOffset();
}

Size ClassMetadataLayout::getInstanceAlignMaskOffset() const {
   assert(InstanceAlignMask.isStatic());
   return InstanceAlignMask.getStaticOffset();
}

ClassMetadataLayout::MethodInfo
ClassMetadataLayout::getMethodInfo(IRGenFunction &IGF, PILDeclRef method) const{
   auto &stored = getStoredMethodInfo(method);
   auto offset = emitOffset(IGF, stored.TheOffset);
   return MethodInfo(offset);
}

Offset ClassMetadataLayout::getFieldOffset(IRGenFunction &IGF,
                                           VarDecl *field) const {
   return emitOffset(IGF, getStoredFieldOffset(field));
}

Size ClassMetadataLayout::getStaticFieldOffset(VarDecl *field) const {
   auto &stored = getStoredFieldOffset(field);
   assert(stored.isStatic() && "resilient class metadata layout unsupported!");
   return stored.getStaticOffset();
}

Size
ClassMetadataLayout::getRelativeGenericRequirementsOffset() const {
   return GenericRequirements.getRelativeOffset();
}

Size
ClassMetadataLayout::getStaticFieldOffsetVectorOffset() const {
   return FieldOffsetVector.getStaticOffset();
}

Size
ClassMetadataLayout::getRelativeFieldOffsetVectorOffset() const {
   return FieldOffsetVector.getRelativeOffset();
}

Size
ClassMetadataLayout::getStaticVTableOffset() const {
   return VTableOffset.getStaticOffset();
}

Size
ClassMetadataLayout::getRelativeVTableOffset() const {
   return VTableOffset.getRelativeOffset();
}

Offset
ClassMetadataLayout::getFieldOffsetVectorOffset(IRGenFunction &IGF) const {
   return emitOffset(IGF, FieldOffsetVector);
}

Size irgen::getClassFieldOffsetOffset(IRGenModule &IGM, ClassDecl *theClass,
                                      VarDecl *field) {
   if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType)
      return Size();

   return IGM.getClassMetadataLayout(theClass).getStaticFieldOffset(field);
}

/// Given a reference to class metadata of the given type,
/// compute the field offset for a stored property.
/// The type must have dependent generic layout.
llvm::Value *irgen::emitClassFieldOffset(IRGenFunction &IGF,
                                         ClassDecl *theClass,
                                         VarDecl *field,
                                         llvm::Value *metadata) {
   auto slot = emitAddressOfClassFieldOffset(IGF, metadata, theClass, field);
   return IGF.emitInvariantLoad(slot);
}

Address irgen::emitAddressOfClassFieldOffset(IRGenFunction &IGF,
                                             llvm::Value *metadata,
                                             ClassDecl *theClass,
                                             VarDecl *field) {
   auto offset =
      IGF.IGM.getClassMetadataLayout(theClass).getFieldOffset(IGF, field);
   auto slot = IGF.emitAddressAtOffset(metadata, offset, IGF.IGM.SizeTy,
                                       IGF.IGM.getPointerAlignment());
   return slot;
}

Address irgen::emitAddressOfSuperclassRefInClassMetadata(IRGenFunction &IGF,
                                                         llvm::Value *metadata) {
   // The superclass field in a class type is the first field past the isa.
   unsigned index = 1;

   Address addr(metadata, IGF.IGM.getPointerAlignment());
   addr = IGF.Builder.CreateElementBitCast(addr, IGF.IGM.TypeMetadataPtrTy);
   return IGF.Builder.CreateConstArrayGEP(addr, index, IGF.IGM.getPointerSize());
}

Size irgen::getStaticTupleElementOffset(IRGenModule &IGM,
                                        PILType tupleType,
                                        unsigned eltIdx) {
   assert(tupleType.is<TupleType>() && "not a tuple type");

   struct TupleElementOffsetScanner
      : LayoutScanner<TupleElementOffsetScanner, TupleMetadataScanner> {
   private:
      using super = LayoutScanner;

      // 8 seems a reasonable potential max number tuple elements to start with
      llvm::SmallVector<Size, 8> Offsets;

   public:
      TupleElementOffsetScanner(IRGenModule &IGM, TupleType *const tupleType)
         : super(IGM, tupleType) {}

      void addElement(unsigned eltIdx, const TupleTypeElt &elt) {
         Offsets.push_back(NextOffset);
         super::addElement(eltIdx, elt);
      }

      Size getElementOffset(unsigned eltIdx) const {
         return Offsets[eltIdx];
      }
   };

   TupleElementOffsetScanner s(IGM, tupleType.getAs<TupleType>().getPointer());
   s.layout();

   return s.getElementOffset(eltIdx);
}

/*********************************** ENUMS ************************************/

EnumMetadataLayout::EnumMetadataLayout(IRGenModule &IGM, EnumDecl *decl)
   : NominalMetadataLayout(Kind::Enum, decl) {

   struct Scanner : LayoutScanner<Scanner, EnumMetadataScanner> {
      using super = LayoutScanner;

      EnumMetadataLayout &Layout;
      Scanner(IRGenModule &IGM, EnumDecl *decl, EnumMetadataLayout &layout)
         : super(IGM, decl), Layout(layout) {}

      void noteStartOfTypeSpecificMembers() {
         assert(getNextOffset().getStaticOffset() ==
                IGM.getOffsetOfEnumTypeSpecificMetadataMembers());
      }

      void addPayloadSize() {
         Layout.PayloadSizeOffset = getNextOffset();
         super::addPayloadSize();
      }

      void noteStartOfGenericRequirements() {
         Layout.GenericRequirements = getNextOffset();
         super::noteStartOfGenericRequirements();
      }

      void layout() {
         super::layout();
         Layout.TheSize = getMetadataSize();
      }
   };

   Scanner(IGM, decl, *this).layout();
}

Offset
EnumMetadataLayout::getPayloadSizeOffset() const {
   assert(PayloadSizeOffset.isStatic());
   return Offset(PayloadSizeOffset.getStaticOffset());
}

/********************************** STRUCTS ***********************************/

StructMetadataLayout::StructMetadataLayout(IRGenModule &IGM, StructDecl *decl)
   : NominalMetadataLayout(Kind::Struct, decl) {

   struct Scanner : LayoutScanner<Scanner, StructMetadataScanner> {
      using super = LayoutScanner;

      StructMetadataLayout &Layout;
      Scanner(IRGenModule &IGM, StructDecl *decl, StructMetadataLayout &layout)
         : super(IGM, decl), Layout(layout) {}

      void noteStartOfTypeSpecificMembers() {
         assert(getNextOffset().getStaticOffset() ==
                IGM.getOffsetOfStructTypeSpecificMetadataMembers());
      }

      void noteStartOfGenericRequirements() {
         Layout.GenericRequirements = getNextOffset();
         super::noteStartOfGenericRequirements();
      }

      void noteStartOfFieldOffsets() {
         Layout.FieldOffsetVector = getNextOffset();
         super::noteStartOfFieldOffsets();
      }

      void addFieldOffset(VarDecl *field) {
         Layout.FieldOffsets.try_emplace(field, getNextOffset());
         super::addFieldOffset(field);
      }

      void noteEndOfFieldOffsets() {
         super::noteEndOfFieldOffsets();
      }

      void layout() {
         super::layout();
         Layout.TheSize = getMetadataSize();
      }
   };

   Scanner(IGM, decl, *this).layout();
}

Offset StructMetadataLayout::getFieldOffset(IRGenFunction &IGF,
                                            VarDecl *field) const {
   // TODO: implement resilient metadata layout
   return Offset(getStaticFieldOffset(field));
}
Size StructMetadataLayout::getStaticFieldOffset(VarDecl *field) const {
   auto &stored = getStoredFieldOffset(field);
   assert(stored.isStatic() && "resilient struct metadata layout unsupported!");
   return stored.getStaticOffset();
}

Offset
StructMetadataLayout::getFieldOffsetVectorOffset() const {
   assert(FieldOffsetVector.isStatic());
   return Offset(FieldOffsetVector.getStaticOffset());
}

/****************************** FOREIGN CLASSES *******************************/
ForeignClassMetadataLayout::ForeignClassMetadataLayout(IRGenModule &IGM,
                                                       ClassDecl *theClass)
   : MetadataLayout(Kind::ForeignClass), Class(theClass) {
   assert(theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType &&
          "Not a foreign class");

   struct Scanner : LayoutScanner<Scanner, ForeignClassMetadataScanner> {
      using super = LayoutScanner;

      ForeignClassMetadataLayout &Layout;
      Scanner(IRGenModule &IGM, ClassDecl *decl,
              ForeignClassMetadataLayout &layout)
         : super(IGM, decl), Layout(layout) {}

      void addSuperclass() {
         Layout.SuperClassOffset = getNextOffset();
         super::addSuperclass();
      }

      void layout() {
         super::layout();
         Layout.TheSize = getMetadataSize();
      }
   };

   Scanner(IGM, theClass, *this).layout();
}
